/*
 * conexiones.c
 *
 *  Created on: 22/11/2011
 *      Author: MagayMade
 */

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <semaphore.h>
#include "conexiones.h"
#include "bootsecto.h"
#include "utils.h"

extern int tamanioCache, lecturaEnProceso;
extern t_conexion *conexiones;
extern pthread_mutex_t semConexiones;
extern int maxSectoresCache, tamanioTDD;
extern TDD_entry *TDD;
extern sem_t semSockContador;
extern bootSector bootSec;

t_conexion available_connection()
{
	int i=0;

	sem_wait(&semSockContador);
	pthread_mutex_lock(&semConexiones);
	while(conexiones[i].libre != FREE )
		i++;
	conexiones[i].libre = BUSY;
	pthread_mutex_unlock(&semConexiones);

	return conexiones[i];
}

void armarPayload(nipcMessage msg, unsigned int sector, char *buffer)
{
	if(msg.type == WRITE) /* "NumeroSector;Buffer" */
	{
		  sprintf(msg.payload, "%d;%s", sector, buffer);
	}
	if (msg.type == READ) /* "NumeroSector;" */
	{
		  sprintf(msg.payload, "%d;",sector);
	}
}

char *check_cache(t_cache *cache, int tamanio, int sector, int pos)
{
	int i;

	for(i=0; i<tamanio; i++)
	{
		if(cache[i].sector == sector)
		{
			pos = i;
			return cache[i].buffer;
		}
	}
	pos = -1;
	return NULL;
}

int cargarCache(char buf[512], int sector, int sectoresEnCache, t_cache *cache, bitMod bitDeModificado)
{
	time_t 		*timeS, *min;
	int 		i, posicion = 0;
	t_conexion 	conexion;
	nipcMessage msg;

	if(sectoresEnCache < maxSectoresCache)
	{
		posicion = sectoresEnCache;
		sectoresEnCache++;

	}
	else
	{
		min = cache[0].timeStand;
		for(i=1; i<sectoresEnCache; i++)
		{
			if (cache[i].timeStand < min)
			{
				min = cache[i].timeStand ;
				posicion = i;
			}
		}
	}

	if(cache[posicion].modificado != 0)
	{
		conexion = available_connection();
		memset(msg.payloadID, '\0', 6);
		sprintf(msg.payloadID ,"%d", conexion.socket);
		msg.type = WRITE;
		armarPayload(msg,sector, cache[posicion].buffer);
		nipc_send(&msg, atoi(msg.payloadID));
		nipc_recv(&msg, atoi(msg.payloadID));
		//TODO contemplar errores en el recv
		pthread_mutex_lock(&semConexiones);
		conexion.libre = FREE;
		pthread_mutex_unlock(&semConexiones);
		sem_post(&semSockContador);
	}

	strcpy(cache[posicion].buffer , buf);
	cache[posicion].sector = sector;
	time(timeS);
	cache[posicion].timeStand = timeS;
	if(bitDeModificado == 1)
	{
	cache[posicion].modificado = bitDeModificado;
	}

	return 0;
}

int crearCache(t_cache *cache)
{
	int i;
	//TODO ver que onda si hay que hacer malloc a ALGO
	maxSectoresCache = (tamanioCache * 1024)/512;

	for(i=0; i<maxSectoresCache; i++)
	{
		strcpy(cache[i].buffer , "");
		cache[i].sector = -1;
		cache[i].timeStand = NULL;
		cache[i].modificado = NOMODIFICADO;
	}

	return 0;
}

void armarBuf(sec_arch *sectores, int tamanio, char *buf, int w, int w2, unsigned int bloque, int secPerBloque)
{
	int i, j;
	unsigned int sector;
	div_t division;
	char *buffer;

	if((w == bloque) || ( w == 0))
	{
		strcat(buf, sectores[0].buffer);
		free(sectores[0].buffer);
		sector = 1;
	}
	else
	{
		if(w < bloque)
			{
				division = div(w, 512);
				if(division.rem != 0)
				{
					sector = division.quot +1;
					strncpy(buf, &sectores[sector].buffer[division.rem], (512-division.rem));
					for(j=0; j<sector;j++)
					{
					free(sectores[sector].buffer);
					}
					sector+=1;
				}
				else
				{
					sector = division.quot;
					strcat(buf, sectores[0].buffer);
					free(sectores[sector].buffer);
					sector+=1;

				}
			}
		else
		{
			w = w - bloque;
			division = div(w, 512);
			if(division.rem != 0)
			{
				sector = division.quot +1;
				strncpy(buf, &sectores[sector].buffer[division.rem], (512-division.rem));
				for(j=0; j<sector;j++)
				{
				free(sectores[sector].buffer);
				}
				sector+=1;
			}
			else
			{
				sector = division.quot;
				strcat(buf, sectores[0].buffer);
				free(sectores[sector].buffer);
				sector+=1;

			}
		}
	}


	for(i=sector; i<(tamanio-secPerBloque); i++)
	{
		strcat(buf, sectores[i].buffer);
		free(sectores[i].buffer);
		j=i;
	}

	if( ((w2 - bloque) == 0) || (w2 == bloque))
	{
		for(i=(tamanio-secPerBloque); i<tamanio; i++)
		{
			strcat(buf, sectores[j].buffer);
			free(sectores[j].buffer);
			j++;
		}
	}
	else
	{
		if( w2 < bloque)
		{
			division = div(w2, 512);
			if(division.rem != 0)
			{
				sector = division.quot +1;
				strncpy(buffer, &sectores[sector].buffer, division.rem);
				strcat(buf, buffer);
				for(j=sector; j<tamanio;j++)
				{
				free(sectores[j].buffer);
				}
			}
		}
		else
		{
			w2 -= bloque;
			division = div(w2, 512);
			if(division.rem != 0)
			{
				sector = division.quot +1;
				strncpy(buffer, &sectores[sector].buffer, division.rem);
				strcat(buf, buffer);
				for(j=sector; j<tamanio;j++)
				{
				free(sectores[j].buffer);
				}
			}
		}
	}
}

int buscarArchEnTDD(const char *path)
{
	char *tok;
	int i, posicion;

	if(tamanioTDD == 0)
	{
		posicion = 0;
	}
	else
	{
	for (i=0; i<tamanioTDD; i++)
	{
		if(TDD[i].path == path)
		{
			posicion = i;
		}
		else
		{
			posicion = -1;
		}
	}
	}
	return posicion;
}

int leerSector(unsigned int sector,int posicion, int sectoresEnCache, char *buffer)
{
	char  		*buf;
	time_t 		*timeS;
	t_conexion 	conexion;
	nipcMessage msg;
	t_cache 	*cache;

	if(tamanioCache !=0)
	{
		buf = check_cache(TDD[posicion].cache, sectoresEnCache, sector, posicion);
		if(buf != NULL)
		{
			lecturaEnProceso = 1;
			strcpy(buffer, buf);
			time(timeS);
			TDD[posicion].cache->timeStand = timeS;
			lecturaEnProceso = 0;
		}else{
			conexion = available_connection();
			sprintf(msg.payloadID ,"%d", conexion.socket);
			msg.type = READ;
			armarPayload(msg,sector, NULL);
			nipc_send(&msg, atoi(msg.payloadID));
			nipc_recv(&msg, atoi(msg.payloadID));
			pthread_mutex_lock(&semConexiones);
			conexion.libre = FREE;
			pthread_mutex_unlock(&semConexiones);
			sem_post(&semSockContador);
			sector = atoi(strtok(msg.payload, ";"));
			strcpy(buffer, strtok(NULL, "\0"));
			pthread_mutex_lock(&TDD[posicion].semCache);
			cargarCache(buffer, sector, sectoresEnCache, cache, NOMODIFICADO); //ACA VA SUMANDO CADA VEZ QUE CARGA
			pthread_mutex_unlock(&TDD[posicion].semCache);
		}
	}
	else
	{
		conexion = available_connection();
		sprintf(msg.payloadID ,"%d", conexion.socket);
		msg.type = READ;
		armarPayload(msg, sector, NULL);
		nipc_send(&msg, atoi(msg.payloadID));
		nipc_recv(&msg, atoi(msg.payloadID));
		pthread_mutex_lock(&semConexiones);
		conexion.libre = FREE;
		pthread_mutex_unlock(&semConexiones);
		sem_post(&semSockContador);
		sector = atoi(strtok(msg.payload, ";"));
		strcpy(buffer, strtok(NULL, "\0"));
	}

	return 0;
}

int posicionEnTDD(const char *path)
{
	int 	posicion;

	if(tamanioCache != 0)
	{
		posicion = buscarArchEnTDD(path);

		if(posicion == -1)
		{
			tamanioTDD++;
			TDD[tamanioTDD].path = path;
			crearCache(TDD[tamanioTDD].cache);
			posicion = tamanioTDD;
		}
	}

	return posicion;
}

unsigned int *crearListaDeSectores(unsigned int primerSector, int secPerBloque)
{
	unsigned int 	*lista =NULL;
	int 			i;

	lista[0]= primerSector;

	for(i=1; i< secPerBloque; i++)
	{
		lista[i] = primerSector + 1;
	}

	return lista;
}

void escribirEnDisco(char buff[512], unsigned int sector)
{
	t_conexion	conexion;
	nipcMessage	msg;

	conexion = available_connection();

	/* armo el mensaje */
	sprintf(msg.payloadID ,"%d", conexion.socket);
	msg.type = WRITE;
	armarPayload(msg, sector, NULL);

	nipc_send(&msg, atoi(msg.payloadID));
	nipc_recv(&msg, atoi(msg.payloadID));

	pthread_mutex_lock(&semConexiones);
	conexion.libre = FREE;
	pthread_mutex_unlock(&semConexiones);

	sem_post(&semSockContador);
}

void escribirBufEnCache(const char *path, const char *buf, int numSector, unsigned int *listaSectoresClusterLibre)
{
	int 	posicion, sectoresEnCache, numeroUltimoSector, ultimoByteEscrito;;
	char 	buff[512];

	posicion = posicionEnTDD(path);

	/* divido el buff cada 512 para cargar cada sector en cache */
	strncpy(buff, &buf[numSector*512], 512);
	pthread_mutex_lock(&TDD[posicion].semCache);
	cargarCache(buff, listaSectoresClusterLibre[numSector], sectoresEnCache, TDD[posicion].cache, MODIFICADO);
	pthread_mutex_unlock(&TDD[posicion].semCache);

	/* TODO cuando salgo del ciclo actualizar estos datos en la FAT o donde sea necesario */
	numeroUltimoSector = listaSectoresClusterLibre[numSector];
	ultimoByteEscrito = strlen(buff);
}

void guardarInfoEnClusters(int sectoresEnBuf, const char *path, const char *buf, int tamanio)
{
	div_t 			division;
	int 			clustersEnBuf, i, j, sectoresEnCache, numeroUltimoSector, ultimoByteEscrito;
	unsigned int 	*freeClusters, clusterLibre, sectorLibre, *listaSectoresClusterLibre;
	char 			buff[512];

	/* cantidad de clusters que necesita */
	division = div(sectoresEnBuf, bootSec.sectorPerCluster);
	if(division.rem != 0)
	{
		clustersEnBuf = division.quot +1;
	}
	else
	{
		clustersEnBuf = division.quot;
	}

	/* por cada cluster que necesita... */
	for(i = 0; i < clustersEnBuf; i++)
	{
		/* pido un cluster porque no tengo mas espacio en el ultimo cluster del archivo */
	/*	freeClusters = getFreeClusters();
		clusterLibre = unqueue(freeClusters);
		sectorLibre = getSectorClusterN(clusterLibre);
		listaSectoresClusterLibre = crearListaDeSectores(sectorLibre);*/

		/* por cada sector del cluster pedido */
		for(j = 0; j < bootSec.sectorPerCluster; j++)
		{
			/* si tengo chache escribo ahi */
			if(tamanioCache != 0)
			{
	//			escribirBufEnCache(path, buf, j, listaSectoresClusterLibre, sectoresEnCache, numeroUltimoSector, ultimoByteEscrito);
			}
			else /* si no tengo cache */
			{
				/* divido el buff cada 512 para cargar cada sector en cache */
				strncpy(buff, &buf[j*512], 512);
				escribirEnDisco(buff, listaSectoresClusterLibre[j]);

				/* TODO cuando salgo del ciclo actualizar estos datos en la FAT o donde sea necesario */
				numeroUltimoSector = listaSectoresClusterLibre[j];
				ultimoByteEscrito = strlen(buff);
			}
		} /* cierro el for de "por cada sector del cluster pedido" */

		/* incremento el numero de clusters asignados al archivo"
		 * TODO actualizar este datos en la FAT o donde sea necesario */
		tamanio++;

	} /* cierro el for de "por cada cluster que necesita... " */
}

void guardarInfoSectorLleno(int posicionS, int sectoresEnBuf, const char *path, const char *buf, int tamanio, unsigned int *listaSectoresClusterN)
{
	unsigned int 	secLibres, secAPedir, *listaSectoresClusterLibre;
	int 			i, j, numeroUltimoSector, ultimoByteEscrito;
	char 			buff[512];

	/* si es el ultimo sector */
	if((posicionS + VECTORCOMIENZAENCERO) == bootSec.sectorPerCluster)
	{
		/* pide clusters, chequea si hay cache y guarda */
		guardarInfoEnClusters(sectoresEnBuf, path, buf, tamanio);
	}
	else /* y si no es el ultimo sector del cluster pero esta lleno */
	{
		/* porque el ultimo sector esta lleno, incrementamos posicionS para que indique al sector siguiente */
		posicionS ++;
		secLibres = (bootSec.sectorPerCluster - posicionS);

		/* por cada sector que me queda libre en el cluster */
		for(i = posicionS; i < bootSec.sectorPerCluster; i++)
		{
			/* si tengo chache escribo ahi */
			if(tamanioCache != 0)
			{
				escribirBufEnCache(path, buf, i, listaSectoresClusterN);
			}
			else /* si no tengo cache */
			{
				/* divido el buff cada 512 para guardar cada sector en disco */
				strncpy(buff, &buf[j*512], 512);
				escribirEnDisco(buff, listaSectoresClusterN[i]);

				/* TODO cuando salgo del ciclo actualizar estos datos en la FAT o donde sea necesario */
				numeroUltimoSector = listaSectoresClusterN[i];
				ultimoByteEscrito = strlen(buff);
			}

			secLibres--;
		}

		/* si todavia tengo sectores para guardar */
		if(secLibres != 0)
		{
			secAPedir = sectoresEnBuf - secLibres;

			guardarInfoEnClusters(secAPedir, path, buf, tamanio);
		}
	}
}

char *getSector(unsigned int sector){

	nipcMessage msg;
	char 		*buffer;
	int		 	fd;
	t_conexion 	conexion;

	conexion = available_connection();

	fd = conexion.socket;

	 char string[intlen(sector)];
	 sprintf(string,"%d",10);
     strcat(string, ";");


	sprintf(msg.payloadID, "%d", fd);
	msg.type = '1';
	msg.payload = string;  // ESTE ES EL NRO DE SECTOR QUE QUIERO
	msg.payloadLength = strlen(msg.payload);

	nipc_send(&msg, fd);
	nipc_recv(&msg, fd);


	// nro sector; contenido
	conexiones->libre = FREE;

	buffer = strtok(msg.payload, "\0");

	printf("El contenido del buffer es... %s", buffer);

	return buffer;
}

void setSector(unsigned int sector, char *contenido){

	nipcMessage msg;
	int		 	fd;
	t_conexion 	conexion;

	conexion = available_connection();

	fd = conexion.socket;

	 char string[intlen(sector)];
	 sprintf(string,"%d",10);
     strcat(string, ";");
     strcat(string,contenido);


	sprintf(msg.payloadID, "%d", fd);
	msg.type = '1';
	msg.payload = string;  // ESTE ES EL NRO DE SECTOR QUE QUIERO MODIFICAR, CON SU CONTENIDO
	msg.payloadLength = strlen(msg.payload);

	nipc_send(&msg, fd);
	nipc_recv(&msg, fd);


	// nro sector; contenido
	conexiones->libre = FREE;

}

void getBloque(unsigned int sector, int bloque,int secPerBloque, sec_arch *sectores, int tamanio, int posicion, int sectoresEnCache){

	int 			i;
	unsigned int 	*lista;

	/* creamos la lista de sectores a pedir segun el bloque solicitado */
	switch(bloque)
	{
	case 1:
		lista = crearListaDeSectores(sector, secPerBloque);
		tamanio += secPerBloque;
		break;
	case 2:
		lista = crearListaDeSectores(sector + secPerBloque, secPerBloque);
		tamanio += secPerBloque;
		break;
	default:
		break;
	}

	/* si hay cache busca ahi, sino pide al RAID/PPD */
	for(i=0; i<secPerBloque;i++)
	{
		leerSector(sectores[lista[i]].sector, posicion, sectoresEnCache, sectores[lista[i]].buffer);
	}
}

void setBloque(unsigned int sector, int bloque,int secPerBloque, sec_arch *sectores, int tamanio, int posicion, int sectoresEnCache)
{

}
